//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices.ComTypes;

namespace OpenLS.Spreadsheet.Formats.Biff
{
    internal class CFStream : Stream
    {
        // Fields
        private readonly FileAccess _access;
        private IStream _safeIStream;

        // Methods
        internal CFStream(IStream underlyingStream, FileAccess openAccess)
        {
            _safeIStream = underlyingStream;
            _access = openAccess;
        }

        public override bool CanRead
        {
            get
            {
                if (StreamDisposed)
                {
                    return false;
                }
                if (FileAccess.Read != (_access & FileAccess.Read))
                {
                    return (FileAccess.ReadWrite == (_access & FileAccess.ReadWrite));
                }
                return true;
            }
        }

        public override bool CanSeek
        {
            get { return !StreamDisposed; }
        }

        public override bool CanWrite
        {
            get
            {
                if (StreamDisposed)
                {
                    return false;
                }
                if (FileAccess.Write != (_access & FileAccess.Write))
                {
                    return (FileAccess.ReadWrite == (_access & FileAccess.ReadWrite));
                }
                return true;
            }
        }

        public override long Length
        {
            get
            {
                STATSTG statstg;
                CheckDisposedStatus();
                statstg = new STATSTG();
                _safeIStream.Stat(ref statstg, 1);
                return statstg.cbSize;
            }
        }

        public override long Position
        {
            get
            {
                CheckDisposedStatus();
                long plibNewPosition = 0L;
                throw new NotImplementedException(); 
            }
            set
            {
                CheckDisposedStatus();
                if (!CanSeek)
                {
                    throw new NotImplementedException();
                }
                long plibNewPosition = 0L;
                throw new NotImplementedException(); //\\this._safeIStream.Seek(value, 0, out plibNewPosition);
            }
        }

        internal bool StreamDisposed
        {
            get
            {
                return false;
                throw new NotImplementedException(); //\\if (!this.backReference.StreamInfoDisposed)
                {
                    return (null == _safeIStream);
                }
                return true;
            }
        }

        internal void CheckDisposedStatus()
        {
            if (StreamDisposed)
            {
                throw new ObjectDisposedException(null, "object disposed");
            }
        }

        protected override void Dispose(bool disposing)
        {
            try
            {
                if (disposing && (_safeIStream != null))
                {
                    _safeIStream = null;
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        public override void Flush()
        {
            CheckDisposedStatus();
            _safeIStream.Commit(0);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposedStatus();
            //\\ PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count);
            uint pcbRead = 0;
            if (offset == 0)
            {
                //\\ _safeIStream.Read(
                _safeIStream.Read(buffer, (uint) count, ref pcbRead);
                return (int) pcbRead;
            }
            var pv = new byte[count];
            _safeIStream.Read(pv, (uint) count, ref pcbRead);
            if (pcbRead > 0)
            {
                Array.Copy(pv, 0, buffer, offset, pcbRead);
            }
            return (int) pcbRead;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            CheckDisposedStatus();
            if (!CanSeek)
            {
                throw new NotSupportedException();
            }
            long plibNewPosition = 0L;
            int dwOrigin = 0;
            switch (origin)
            {
                case SeekOrigin.Begin:
                    if (0L > offset)
                    {
                        throw new ArgumentOutOfRangeException("offset", "");
                    }
                    break;

                case SeekOrigin.Current:
                    dwOrigin = 1;
                    break;

                case SeekOrigin.End:
                    dwOrigin = 2;
                    break;

                default:
                    throw new InvalidEnumArgumentException("origin", (int) origin, typeof (SeekOrigin));
            }
            throw new NotImplementedException(); //\\this._safeIStream.Seek(offset, dwOrigin, out plibNewPosition);
            return plibNewPosition;
        }

        public override void SetLength(long newLength)
        {
            CheckDisposedStatus();
            if (!CanWrite)
            {
                throw new NotSupportedException("cant write");
            }
            if (0L > newLength)
            {
                throw new ArgumentOutOfRangeException("newLength", "StreamLengthNegative");
            }
            _safeIStream.SetSize((ulong) newLength);
            if (newLength < Position)
            {
                Position = newLength;
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            CheckDisposedStatus();
            //\\ PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count);
            uint pcbWritten = 0;
            if (offset == 0)
            {
                _safeIStream.Write(buffer, (uint) count, ref pcbWritten);
            }
            else
            {
                var destinationArray = new byte[count];
                Array.Copy(buffer, offset, destinationArray, 0, count);
                _safeIStream.Write(destinationArray, (uint) count, ref pcbWritten);
            }
            if (count != pcbWritten)
            {
                throw new IOException("WriteFailure");
            }
        }

        // Properties
    }
}